/*
 * Copyright (C) 2009 Georgios J. Georgopoulos, All rights reserved.
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.gwt.mosaic.xul.client.ui;

import java.util.List;

import org.gwt.mosaic.forms.client.layout.CellConstraints;
import org.gwt.mosaic.forms.client.layout.FormLayout;
import org.gwt.mosaic.ui.client.layout.LayoutPanel;

import com.google.gwt.user.client.ui.Widget;

/**
 * A container element which can contain any number of child elements. If the
 * box has an orient attribute that is set to horizontal, the child elements are
 * laid out left to right in the order they appear in the box. If the orient is
 * set to vertical, the child elements are laid out from top to bottom. Child
 * elements do not overlap. The default orientation is horizontal.
 * 
 * @author georgopoulos.georgios(at)gmail.com
 * 
 */
public class Box extends Container {
  private static final long serialVersionUID = 7366741308398406547L;

  private transient LayoutPanel layoutPanel;

  // -----------------------------------------------------------------------
  public static final String ORIENT = "orient";

  public String getOrient() {
    return getString(ORIENT);
  }

  public void setOrient(String orient) {
    putString(ORIENT, orient);
  }

  // -----------------------------------------------------------------------
  public Box() {
    super();
  }

  // -----------------------------------------------------------------------
  @Override
  protected Widget createUI() {
    if (layoutPanel == null) {
      layoutPanel = new LayoutPanel();
      layoutPanel.setStyleName("xul-Box");
    }

    syncUI(layoutPanel);

    return layoutPanel;
  }

  @Override
  protected void syncUI(Widget ui) {
    final StringBuffer sb = new StringBuffer();

    String dluStr;
    if ("vertical".equals(getOrient())) {
      dluStr = VDLU + "dlu";
    } else {
      dluStr = HDLU + "dlu";
    }

    layoutPanel.clear();

    final List<Element> elementsWithUI = Container.searchForElementsWithUI(this);
    final int flexSum = calculateFlexSum(elementsWithUI);

    for (int i = 0, n = elementsWithUI.size(), nn = n; i < nn; ++i) {
      final Element element = elementsWithUI.get(i);
      final Widget widget = element.getUI();

      // equalsize will ignore any flexibility
      int elementFlex = 0;
      if (!isEqualize() && element.getAttributeMap().containsKey(Element.FLEX)) {
        elementFlex += element.getFlex();
      }

      if (i + 1 < n) {
        if (elementFlex > 0) {
          sb.append("fill:0:grow(" + ((double) elementFlex / (double) flexSum)
              + "), " + dluStr + ", ");
        } else if (isEqualize()) {
          sb.append("fill:pref:grow, " + dluStr + ", ");
        } else {
          sb.append("pref, " + dluStr + ", ");
        }
      } else {
        if (elementFlex > 0) {
          sb.append("fill:0:grow(" + ((double) elementFlex / (double) flexSum)
              + ")");
        } else if (isEqualize()) {
          sb.append("fill:pref:grow");
        } else {
          sb.append("pref");
        }
      }

      // check if packing yields to flex
      String pack = null;
      if (!(element instanceof Container && calculateFlexSum(Container.searchForElementsWithUI((Container) element)) > 0)) {
        pack = element.getPack();
      }

      if ("vertical".equals(getOrient())) {
        if ("start".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(1, 2 * i + 1,
              CellConstraints.LEFT, CellConstraints.DEFAULT));
        } else if ("center".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(1, 2 * i + 1,
              CellConstraints.CENTER, CellConstraints.DEFAULT));
        } else if ("end".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(1, 2 * i + 1,
              CellConstraints.RIGHT, CellConstraints.DEFAULT));
        } else {
          layoutPanel.add(widget, CellConstraints.xy(1, 2 * i + 1));
        }
      } else {
        if ("start".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(2 * i + 1, 1,
              CellConstraints.DEFAULT, CellConstraints.TOP));
        } else if ("center".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(2 * i + 1, 1,
              CellConstraints.DEFAULT, CellConstraints.CENTER));
        } else if ("end".equals(pack)) {
          layoutPanel.add(widget, new CellConstraints(2 * i + 1, 1,
              CellConstraints.DEFAULT, CellConstraints.BOTTOM));
        } else {
          layoutPanel.add(widget, CellConstraints.xy(2 * i + 1, 1));
        }
      }
    }

    FormLayout layout;

    final String align = getAlign();
    if ("vertical".equals(getOrient())) {
      if ("start".equals(align)) {
        layout = new FormLayout("left:pref:grow", sb.toString());
      } else if ("center".equals(align)) {
        layout = new FormLayout("center:pref:grow", sb.toString());
      } else if ("end".equals(align)) {
        layout = new FormLayout("right:pref:grow", sb.toString());
      } else {
        layout = new FormLayout("fill:pref:grow", sb.toString());
      }
    } else {
      if ("start".equals(align)) {
        layout = new FormLayout(sb.toString(), "top:pref:grow");
      } else if ("center".equals(align)) {
        layout = new FormLayout(sb.toString(), "center:pref:grow");
      } else if ("end".equals(align)) {
        layout = new FormLayout(sb.toString(), "bottom:pref:grow");
      } else {
        layout = new FormLayout(sb.toString(), "fill:pref:grow");
      }
    }
    layoutPanel.setLayout(layout);

    if (isEqualize()) {
      for (int i = 0, n = elementsWithUI.size(); i < n; ++i) {
        if ("vertical".equals(getOrient())) {
          layout.addGroupedRow(2 * i + 1);
        } else {
          layout.addGroupedColumn(2 * i + 1);
        }
      }
    }

    super.syncUI(ui);
  }

  private int calculateFlexSum(List<Element> elementsWithUI) {
    int flex = 0;
    for (Element element : elementsWithUI) {
      if (element.getAttributeMap().containsKey(Element.FLEX)) {
        flex += element.getFlex();
      }
    }
    return flex;
  }

  @Override
  protected void elementRemoved(Element element) {
    layoutPanel.remove(element.getUI());
    super.elementRemoved(element);
  }

}
